// Copyright (C) 2015, Alberto Corona <alberto@0x1a.us>
// All rights reserved. This file is part of core-utils, distributed under the
// GPL v3 license. For full terms please see the LICENSE file.

#![crate_type = "bin"]
#![crate_name = "cat"]
#![feature(path_ext)]

static VERS: &'static str = "0.1.0";
static PROG: &'static str = "cat";

extern crate getopts;
extern crate util;

use getopts::{Options};
use util::{Status};

use std::env;
use std::io::Read;
use std::path::{PathBuf};
use std::fs::{File,PathExt};

fn files_to_string(file: Vec<String>) -> String {
    let mut return_string = String::new();
    for item in file {
        let mut fstring = String::new();
        let fpath = PathBuf::from(&item);
        if fpath.is_dir() {
            util::path_err(Status::Error, String::from("cat: file is a directory"), fpath);
        }
        let mut ffile = match File::open(PathBuf::from(&item)) {
            Ok(f) => { f },
            Err(e) => {
                util::err(PROG, Status::Error, e.to_string());
                panic!();
            }
        };
        match ffile.read_to_string(&mut fstring) {
            Ok(_) => { return_string.push_str(&fstring) },
            Err(e) => {
                util::err(PROG, Status::Error, e.to_string());
                panic!();
            }
        };
    }
    return return_string;
}

fn print_lines(file: String, new: bool, tabs: bool, num: bool) {
    let mut lineno: i32 = 1;
    let num_lines: u32 = file.lines().count() as u32;
    if num {
        print!("{}  ", lineno);
    }
    for i in file.chars() {
        if num && new && i == '\n' {
            if lineno < num_lines as i32 {
                lineno += 1;
                print!("$\n{}  ", lineno);
            }
        } else if num && i == '\n' {
            if lineno < num_lines as i32 {
                lineno += 1;
                print!("{}{}  ", i, lineno);
            }
        } else if i == '\n' && new {
            print!("$\n");
        } else if i == '\t' && tabs {
            print!("^I");
        } else {
            print!("{}", i);
        }
    }
}

fn print_usage(prog: &str, opts: Options) {
    let brief = format!("Usage: {} [OPTION]", prog);
    print!("{}", opts.usage(&brief));
    util::exit(Status::Ok);
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let mut opts = Options::new();

    opts.optflag("h", "help", "Print the help menu");
    opts.optflag("", "version", "Print the version");
    opts.optflag("a", "all", "Show all characters");
    opts.optflag("n", "numbered", "Show numbered lines");
    opts.optflag("E", "ends", "Show newline characters as '$'");
    opts.optflag("t", "tabs", "Show tab characters as '^I'");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => {
            util::err(PROG, Status::OptError, f.to_string());
            panic!(f)
        }
    };
    let tabs = matches.opt_present("t");
    let new = matches.opt_present("E");
    let num = matches.opt_present("n");
    if matches.opt_present("h") {
        print_usage(PROG, opts);
    } else if matches.opt_present("version") {
        util::copyright(PROG, VERS, "2015", vec!["Alberto Corona"]);
    } else if matches.opt_present("a") {
        print_lines(files_to_string(matches.free), true, true, num);
    } else {
        print_lines(files_to_string(matches.free), new, tabs, num);
    }
}
